/*测试三要素
    1、测试环境
    2、测试方法
    3、测试结果：同步下的单线程/多线程；异步下的单线程/多线程
  测试工具的编写：
    1、可以控制写日志线程数量
    2、可以控制写日志的总数量
    3、分别对于同步日志器 & 异步日志器进行各自的性能测试
       需要测试单写日志线程的性能
       需要测试多写日志线程的性能
  实现：
    封装一个接口，传入日志器名称，线程数量，单条日志大小
    在接口内，创建指定数量的线程，各自负责一部分日志的输出
    在输出之前计时开始，在输出完毕后计时结束，所耗时间 = 结束时间 - 起始时间
    每秒输出量 = 日志数量 / 总耗时
    每秒输出大小 = 日志数量 * 单条日志大小 / 总耗时
  注意：异步日志输出这里，我们启动非安全模式，纯内存写入（不去考虑实际落地的时间）
*/

#include "../logs/bitlog.h"
#include <vector>
#include <thread>
#include <chrono>

void bench(const std::string &logger_name, size_t thr_count, size_t msg_count, size_t msg_len)
{
    // 1、获取日志器
    bitlog::Logger::ptr logger = bitlog::getLogger(logger_name); // 获取一个日志器
    if (logger.get() == nullptr)                                 // 代表没有这个日志器
    {
        return;
    }
    std::cout << "测试日志: " << msg_count << "条, 总大小: " << (msg_count * msg_len) / 1024 << "KB\n";

    // 2、组织指定长度的日志消息
    std::string msg(msg_len - 1, 'A'); // 少一个字节，是为了给末尾到时候添加换行

    // 3、创建指定数量的线程
    std::vector<std::thread> threads;
    std::vector<double> cost_arry(thr_count);
    size_t msg_pet_thr = msg_count / thr_count; // 总日志数量 / 线程数量就是每隔线程要输出的日志数量
    for (int i = 0; i < thr_count; i++)         // push_back是一个构造好的对象，然后进行拷贝构造，给vector里也去构造一个，
    // emplace_back，构造并插入一个元素到末尾，是在vector原有的空间基础上，然后构造元素，就不需要经历拷贝构造这个过程了
    { // 所以这里传入的是，构造threads时，传入的信息，线程这里只需要有一个入口函数就行了
        threads.emplace_back([&, i]()
                             {
                                 // 4、线程函数内部开始计时
                                 auto start = std::chrono::high_resolution_clock::now();
                                 // 5、开始循环写日志
                                for(int j=0;j<msg_pet_thr;j++)
                                {
                                    logger->fatal("%s",msg.c_str());
                                }
                                 // 6、线程函数内部结束计时
                                 auto end = std::chrono::high_resolution_clock::now();
                                 std::chrono::duration<double> cost = end-start;//结束-开始，就计算出结束计时
                                 cost_arry[i] = cost.count();
                                 std::cout<<"线程"<<i<<" : "<<"\t输出数量: "<<msg_pet_thr<<", 耗时: "<<cost.count()<<"s"<<std::endl; });
    }
    for (int i = 0; i < thr_count; i++)
    {
        threads[i].join(); // 所以线程退出
    }
    // 7、计算总耗时: 在多线程中，每个线程都会耗费时间，但是线程是并发处理的，因此耗时最高的那个就是总时间
    double max_cost = cost_arry[0];
    for (int i = 0; i < thr_count; i++)
    {
        max_cost = max_cost < cost_arry[i] ? cost_arry[i] : max_cost;
    }
    size_t msg_per_sec = msg_count / max_cost;
    size_t size_per_sec = (msg_count * msg_len) / (max_cost * 1024);
    // 8、进行输出打印
    std::cout << "\t总耗时: " << max_cost << "s\n";
    std::cout << "\t每秒输出日志数量: " << msg_per_sec << "条\n";
    std::cout << "\t每秒输出日志大小: " << size_per_sec << "KB\n";
}

void sync_bench() // 同步测试
{
    std::unique_ptr<bitlog::LoggerBuilder> builder(new bitlog::GlobalLoggerBuilder()); // 全局日志器的建造者
    builder->buildLoggerName("sync_logger");
    builder->buildLoggerLevel(bitlog::LogLevel::value::DEBUG);
    builder->buildFormatter("%m%n");
    builder->buildLoggerType(bitlog::LoggerType::LOGGER_SYNC);
    builder->buildSink<bitlog::FileSink>("./logfile/async.log");
    builder->build();
    // bench("sync_logger", 1, 1000000, 100);//单线程测试

    bench("sync_logger", 3, 1000000, 100); // 多线程测试
}

void async_bench() // 异步测试
{
    std::unique_ptr<bitlog::LoggerBuilder> builder(new bitlog::GlobalLoggerBuilder()); // 全局日志器的建造者
    builder->buildLoggerName("async_logger");
    builder->buildLoggerLevel(bitlog::LogLevel::value::DEBUG);
    builder->buildFormatter("%m%n");
    builder->buildLoggerType(bitlog::LoggerType::LOGGER_ASYNC);
    // builder->buildEnableUmsafeAsync();//开启非安全模式---主要为了将实际落地时间排除在外
    builder->buildSink<bitlog::FileSink>("./logfile/async.log");
    builder->build();
    // bench("async_logger", 1, 1000000, 100); // 单线程测试

    bench("async_logger", 3, 1000000, 100); // 多线程测试
}

int main()
{
    // sync_bench();// 多线程比单线程快，因为单线程不涉及锁冲突，这是同步写日志的情况
    async_bench();
    return 0;
}
